/*
 * FreeRTOS V202212.00
 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * https://www.FreeRTOS.org
 * https://github.com/FreeRTOS
 *
 */

/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"

/* Standard includes. */
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

/* FreeRTOS+CLI includes. */
#include "FreeRTOS_CLI.h"

/* File system includes. */
#include <redposix.h>
#include <redtests.h>

#ifdef _WINDOWS_
    #define snprintf    _snprintf
#endif

#define cliNEW_LINE     "\r\n"

/*******************************************************************************
 * See the URL in the comments within main.c for the location of the online
 * documentation.
 ******************************************************************************/

/*
 * Print out information on a single file.
 */
static void prvCreateFileInfoString( char * pcBuffer,
                                     REDDIRENT * pxDirent );

/*
 * Copies an existing file into a newly created file.
 */
static BaseType_t prvPerformCopy( int32_t lSourceFildes,
                                  int32_t lDestinationFiledes,
                                  char * pxWriteBuffer,
                                  size_t xWriteBufferLen );

/*
 * Implements the DIR command.
 */
static BaseType_t prvDIRCommand( char * pcWriteBuffer,
                                 size_t xWriteBufferLen,
                                 const char * pcCommandString );

/*
 * Implements the DEL command.
 */
static BaseType_t prvDELCommand( char * pcWriteBuffer,
                                 size_t xWriteBufferLen,
                                 const char * pcCommandString );

/*
 * Implements the TYPE command.
 */
static BaseType_t prvTYPECommand( char * pcWriteBuffer,
                                  size_t xWriteBufferLen,
                                  const char * pcCommandString );

/*
 * Implements the APPEND command.
 */
static BaseType_t prvAPPENDCommand( char * pcWriteBuffer,
                                    size_t xWriteBufferLen,
                                    const char * pcCommandString );

/*
 * Implements the COPY command.
 */
static BaseType_t prvCOPYCommand( char * pcWriteBuffer,
                                  size_t xWriteBufferLen,
                                  const char * pcCommandString );

/*
 * Implements the CREATE command.
 */
static BaseType_t prvCREATECommand( char * pcWriteBuffer,
                                    size_t xWriteBufferLen,
                                    const char * pcCommandString );

/*
 * Implements the MKDIR command.
 */
static BaseType_t prvMKDIRCommand( char * pcWriteBuffer,
                                   size_t xWriteBufferLen,
                                   const char * pcCommandString );

/*
 * Implements the RENAME command.
 */
static BaseType_t prvRENAMECommand( char * pcWriteBuffer,
                                    size_t xWriteBufferLen,
                                    const char * pcCommandString );

/*
 * Implements the LINK command.
 */
static BaseType_t prvLINKCommand( char * pcWriteBuffer,
                                  size_t xWriteBufferLen,
                                  const char * pcCommandString );

/*
 * Implements the STAT command.
 */
static BaseType_t prvSTATCommand( char * pcWriteBuffer,
                                  size_t xWriteBufferLen,
                                  const char * pcCommandString );

/*
 * Implements the STATFS command.
 */
static BaseType_t prvSTATFSCommand( char * pcWriteBuffer,
                                    size_t xWriteBufferLen,
                                    const char * pcCommandString );

/*
 * Implements the FORMAT command.
 */
static BaseType_t prvFORMATCommand( char * pcWriteBuffer,
                                    size_t xWriteBufferLen,
                                    const char * pcCommandString );

/*
 * Implements the TRANSACT command.
 */
static BaseType_t prvTRANSACTCommand( char * pcWriteBuffer,
                                      size_t xWriteBufferLen,
                                      const char * pcCommandString );

/*
 * Implements the TRANSMASKGET command.
 */
static BaseType_t prvTRANSMASKGETCommand( char * pcWriteBuffer,
                                          size_t xWriteBufferLen,
                                          const char * pcCommandString );

/*
 * Implements the TRANSMASKSET command.
 */
static BaseType_t prvTRANSMASKSETCommand( char * pcWriteBuffer,
                                          size_t xWriteBufferLen,
                                          const char * pcCommandString );

/*
 * Implements the ABORT command.
 */
static BaseType_t prvABORTCommand( char * pcWriteBuffer,
                                   size_t xWriteBufferLen,
                                   const char * pcCommandString );

/*
 * Implements the TEST command.
 */
static BaseType_t prvTESTFSCommand( char * pcWriteBuffer,
                                    size_t xWriteBufferLen,
                                    const char * pcCommandString );


/* Structure that defines the DIR command line command, which lists all the
 * files in the current directory. */
static const CLI_Command_Definition_t xDIR =
{
    "dir",         /* The command string to type. */
    "\r\ndir <filename>:\r\n Lists the files in the named directory\r\n",
    prvDIRCommand, /* The function to run. */
    1              /* One parameter is expected. */
};

/* Structure that defines the TYPE command line command, which prints the
 * contents of a file to the console. */
static const CLI_Command_Definition_t xTYPE =
{
    "type",         /* The command string to type. */
    "\r\ntype <filename>:\r\n Prints file contents to the terminal\r\n",
    prvTYPECommand, /* The function to run. */
    1               /* One parameter is expected. */
};

/* Structure that defines the APPEND command line command, which appends data
 * to a file. */
static const CLI_Command_Definition_t xAPPEND =
{
    "append",         /* The command string to type. */
    "\r\nappend <filename> <character> <length>:\r\n Appends data to a file (created if it does not exist)\r\n",
    prvAPPENDCommand, /* The function to run. */
    3                 /* Three parameters are expected. */
};

/* Structure that defines the DEL command line command, which deletes a file. */
static const CLI_Command_Definition_t xDEL =
{
    "del",         /* The command string to type. */
    "\r\ndel <filename>:\r\n deletes a file or directory\r\n",
    prvDELCommand, /* The function to run. */
    1              /* One parameter is expected. */
};

/* Structure that defines the COPY command line command, which copies a file. */
static const CLI_Command_Definition_t xCOPY =
{
    "copy",         /* The command string to type. */
    "\r\ncopy <source file> <dest file>:\r\n Copies <source file> to <dest file>\r\n",
    prvCOPYCommand, /* The function to run. */
    2               /* Two parameters are expected. */
};

/* Structure that defines the CREATE command line command, which creates an
 * empty file. */
static const CLI_Command_Definition_t xCREATE =
{
    "create",         /* The command string to type. */
    "\r\ncreate <filename>:\r\n Creates an empty file\r\n",
    prvCREATECommand, /* The function to run. */
    1                 /* One parameter is expected. */
};

/* Structure that defines the MKDIR command line command, which creates an
 * empty directory. */
static const CLI_Command_Definition_t xMKDIR =
{
    "mkdir",         /* The command string to type. */
    "\r\nmkdir <filename>:\r\n Creates an empty directory\r\n",
    prvMKDIRCommand, /* The function to run. */
    1                /* One parameter is expected. */
};

/* Structure that defines the RENAME command line command, which renames a file. */
static const CLI_Command_Definition_t xRENAME =
{
    "rename",         /* The command string to type. */
    "\r\nrename <source file> <dest file>:\r\n Rename <source file> to <dest file>\r\n",
    prvRENAMECommand, /* The function to run. */
    2                 /* Two parameters are expected. */
};

/* Structure that defines the LINK command line command, which creates a hard
 * link. */
static const CLI_Command_Definition_t xLINK =
{
    "link",         /* The command string to type. */
    "\r\nlink <source file> <dest file>:\r\n Create hard link <dest file> pointing at <source file>\r\n",
    prvLINKCommand, /* The function to run. */
    2               /* Two parameters are expected. */
};

/* Structure that defines the STAT command line command, which shows various
 * information about a file. */
static const CLI_Command_Definition_t xSTAT =
{
    "stat",         /* The command string to type. */
    "\r\nstat <filename>:\r\n Show file information\r\n",
    prvSTATCommand, /* The function to run. */
    1               /* One parameter is expected. */
};

/* Structure that defines the STATFS command line command, which shows various
 * file system information. */
static const CLI_Command_Definition_t xSTATFS =
{
    "statfs",         /* The command string to type. */
    "\r\nstatfs:\r\n Show file system information.\r\n",
    prvSTATFSCommand, /* The function to run. */
    0                 /* No parameters are expected. */
};

/* Structure that defines the FORMAT command line command, which re-formats the
 * file system. */
static const CLI_Command_Definition_t xFORMAT =
{
    "format",         /* The command string to type. */
    "\r\nformat:\r\n Re-formats the file system volume.  ALL FILES WILL BE DELETED!\r\n",
    prvFORMATCommand, /* The function to run. */
    0                 /* No parameters are expected. */
};

/* Structure that defines the TRANSACT command line command, which commits a
 * transaction point. */
static const CLI_Command_Definition_t xTRANSACT =
{
    "transact",         /* The command string to type. */
    "\r\ntransact:\r\n Commit a Reliance Edge transaction point\r\n",
    prvTRANSACTCommand, /* The function to run. */
    0                   /* No parameters are expected. */
};

/* Structure that defines the TRANSMASKGET command line command, which retrieves
 * the current automatic transaction event mask. */
static const CLI_Command_Definition_t xTRANSMASKGET =
{
    "transmaskget",         /* The command string to type. */
    "\r\ntransmaskget:\r\n Retrieve the Reliance Edge automatic transaction mask\r\n",
    prvTRANSMASKGETCommand, /* The function to run. */
    0                       /* No parameters are expected. */
};

/* Structure that defines the TRANSMASKSET command line command, which sets the
 * automatic transaction event mask. */
static const CLI_Command_Definition_t xTRANSMASKSET =
{
    "transmaskset",         /* The command string to type. */
    "\r\ntransmaskset <hex mask>:\r\n Set the Reliance Edge automatic transaction mask\r\n",
    prvTRANSMASKSETCommand, /* The function to run. */
    1                       /* One parameter is expected. */
};

/* Structure that defines the ABORT command line command, which rolls back
 * changes which have not been transacted. */
static const CLI_Command_Definition_t xABORT =
{
    "abort",         /* The command string to type. */
    "\r\nabort:\r\n Roll back all changes not part of the last transaction point\r\n",
    prvABORTCommand, /* The function to run. */
    0                /* No parameters are expected. */
};

/* Structure that defines the TEST-FS command line command, which executes some
 * file system driver tests. */
static const CLI_Command_Definition_t xTEST_FS =
{
    "test-fs",        /* The command string to type. */
    "\r\ntest-fs:\r\n Executes file system tests.  ALL FILES WILL BE DELETED!\r\n",
    prvTESTFSCommand, /* The function to run. */
    0                 /* No parameters are expected. */
};

/*-----------------------------------------------------------*/

void vRegisterFileSystemCLICommands( void )
{
    /* Register all the command line commands defined immediately above. */
    FreeRTOS_CLIRegisterCommand( &xDIR );
    FreeRTOS_CLIRegisterCommand( &xTYPE );
    FreeRTOS_CLIRegisterCommand( &xAPPEND );
    FreeRTOS_CLIRegisterCommand( &xDEL );
    FreeRTOS_CLIRegisterCommand( &xCOPY );
    FreeRTOS_CLIRegisterCommand( &xCREATE );
    FreeRTOS_CLIRegisterCommand( &xMKDIR );
    FreeRTOS_CLIRegisterCommand( &xRENAME );
    FreeRTOS_CLIRegisterCommand( &xLINK );
    FreeRTOS_CLIRegisterCommand( &xSTAT );
    FreeRTOS_CLIRegisterCommand( &xSTATFS );
    FreeRTOS_CLIRegisterCommand( &xFORMAT );
    FreeRTOS_CLIRegisterCommand( &xTRANSACT );
    FreeRTOS_CLIRegisterCommand( &xTRANSMASKGET );
    FreeRTOS_CLIRegisterCommand( &xTRANSMASKSET );
    FreeRTOS_CLIRegisterCommand( &xABORT );
    FreeRTOS_CLIRegisterCommand( &xTEST_FS );
}
/*-----------------------------------------------------------*/

static BaseType_t prvDIRCommand( char * pcWriteBuffer,
                                 size_t xWriteBufferLen,
                                 const char * pcCommandString )
{
    static REDDIR * pxDir = NULL;
    REDDIRENT * pxDirent;
    const char * pcParameter;
    BaseType_t xParameterStringLength, xReturn = pdFALSE;

    /* This assumes pcWriteBuffer is long enough. */
    ( void ) pcCommandString;

    /* Ensure the buffer leaves space for the \r\n. */
    configASSERT( xWriteBufferLen > ( strlen( cliNEW_LINE ) * 2 ) );
    xWriteBufferLen -= strlen( cliNEW_LINE );

    if( pxDir == NULL )
    {
        /* Retrieve the directory to DIR. */
        pcParameter = FreeRTOS_CLIGetParameter
                      (
            pcCommandString,        /* The command string itself. */
            1,                      /* Return the first parameter. */
            &xParameterStringLength /* Store the parameter string length. */
                      );

        /* Sanity check something was returned. */
        configASSERT( pcParameter );

        /* This is the first time this function has been executed since the Dir
         * command was run.  Open the directory. */
        pxDir = red_opendir( pcParameter );
    }

    if( pxDir )
    {
        /* red_readdir() returns NULL either on error or upon reaching the
         * end of the directory.  Clear errno so these conditions can be
         * distinguished. */
        red_errno = 0;
        pxDirent = red_readdir( pxDir );

        if( pxDirent )
        {
            prvCreateFileInfoString( pcWriteBuffer, pxDirent );
            xReturn = pdPASS;
        }
        else if( red_errno == 0 )
        {
            /* There are no more files.  Close the directory. */
            red_closedir( pxDir );
            pxDir = NULL;

            /* No string to return. */
            pcWriteBuffer[ 0 ] = 0x00;
        }
        else
        {
            snprintf( pcWriteBuffer, xWriteBufferLen, "Error %d reading directory.", ( int ) red_errno );
        }
    }
    else
    {
        /* User-friendly messages for common errors. */
        switch( red_errno )
        {
            case RED_ENOENT:
                snprintf( pcWriteBuffer, xWriteBufferLen, "Directory not found." );
                break;

            case RED_ENOTDIR:
                snprintf( pcWriteBuffer, xWriteBufferLen, "Directory not found or not a directory." );
                break;

            default:
                snprintf( pcWriteBuffer, xWriteBufferLen, "Error %d opening directory.", ( int ) red_errno );
                break;
        }
    }

    strcat( pcWriteBuffer, cliNEW_LINE );

    return xReturn;
}
/*-----------------------------------------------------------*/

static BaseType_t prvTYPECommand( char * pcWriteBuffer,
                                  size_t xWriteBufferLen,
                                  const char * pcCommandString )
{
    const char * pcParameter;
    BaseType_t xParameterStringLength, xReturn = pdTRUE;
    static int32_t lFildes = -1;
    REDSTAT finfo;
    int32_t lStatus, lBytesRead;
    size_t xColumns = 50U;

    /* Ensure there is always a null terminator after each character written. */
    memset( pcWriteBuffer, 0x00, xWriteBufferLen );

    /* Ensure the buffer leaves space for the \r\n. */
    configASSERT( xWriteBufferLen > ( strlen( cliNEW_LINE ) * 2 ) );
    xWriteBufferLen -= strlen( cliNEW_LINE );

    if( xWriteBufferLen < xColumns )
    {
        /* Ensure the loop that uses xColumns as an end condition does not
         * write off the end of the buffer. */
        xColumns = xWriteBufferLen;
    }

    if( lFildes == -1 )
    {
        /* The file has not been opened yet.  Find the file name. */
        pcParameter = FreeRTOS_CLIGetParameter
                      (
            pcCommandString,        /* The command string itself. */
            1,                      /* Return the first parameter. */
            &xParameterStringLength /* Store the parameter string length. */
                      );

        /* Sanity check something was returned. */
        configASSERT( pcParameter );

        /* Attempt to open the requested file. */
        lFildes = red_open( pcParameter, RED_O_RDONLY );

        if( lFildes == -1 )
        {
            /* User-friendly messages for common errors. */
            switch( red_errno )
            {
                case RED_ENOENT:
                case RED_ENOTDIR:
                    snprintf( pcWriteBuffer, xWriteBufferLen, "File not found." );
                    break;

                default:
                    snprintf( pcWriteBuffer, xWriteBufferLen, "Error %d opening file.", ( int ) red_errno );
                    break;
            }
        }
        else
        {
            /* Make sure this is a file, not a directory. */
            lStatus = red_fstat( lFildes, &finfo );

            if( lStatus == 0 )
            {
                if( RED_S_ISDIR( finfo.st_mode ) )
                {
                    snprintf( pcWriteBuffer, xWriteBufferLen, "Cannot TYPE a directory." );
                    red_close( lFildes );
                    lFildes = -1;
                }
            }
            else
            {
                snprintf( pcWriteBuffer, xWriteBufferLen, "Error %d querying file.", ( int ) red_errno );
                red_close( lFildes );
                lFildes = -1;
            }
        }
    }

    if( lFildes != -1 )
    {
        /* Read the next chunk of data from the file. */
        lBytesRead = red_read( lFildes, pcWriteBuffer, xColumns );

        if( lBytesRead < ( int32_t ) xColumns )
        {
            /* Error or no more characters to return. */
            red_close( lFildes );
            lFildes = -1;
        }
    }

    if( lFildes == -1 )
    {
        /* Either the file was not opened, or all the data from the file has
         * been returned and the file is now closed. */
        xReturn = pdFALSE;
    }

    strcat( pcWriteBuffer, cliNEW_LINE );

    return xReturn;
}
/*-----------------------------------------------------------*/

static BaseType_t prvAPPENDCommand( char * pcWriteBuffer,
                                    size_t xWriteBufferLen,
                                    const char * pcCommandString )
{
    char * pcFileName = NULL;
    const char * pcCharacter = NULL, * pcLength;
    BaseType_t xParameterStringLength, xGoodParameters = pdTRUE;
    int32_t lFildes, lAppendLength = -1, lThisWrite, lTotalWritten, lBytesWritten;

    /* This function assumes xWriteBufferLen is large enough! */
    ( void ) xWriteBufferLen;

    /* Find the length to write. */
    pcLength = FreeRTOS_CLIGetParameter
               (
        pcCommandString,        /* The command string itself. */
        3,                      /* Return the third parameter. */
        &xParameterStringLength /* Store the parameter string length. */
               );
    configASSERT( pcLength );

    /* Convert the string into a number. */
    lAppendLength = RedAtoI( pcLength );

    if( lAppendLength < 0 )
    {
        strcpy( pcWriteBuffer, "Third parameter cannot be a negative number." );
        xGoodParameters = pdFALSE;
    }

    if( xGoodParameters )
    {
        /* Find the character to write. */
        pcCharacter = FreeRTOS_CLIGetParameter
                      (
            pcCommandString,        /* The command string itself. */
            2,                      /* Return the second parameter. */
            &xParameterStringLength /* Store the parameter string length. */
                      );
        configASSERT( pcCharacter );

        if( xParameterStringLength != 1 )
        {
            strcpy( pcWriteBuffer, "Second parameter must be a single character." );
            xGoodParameters = pdFALSE;
        }
    }

    if( xGoodParameters )
    {
        /* Find the file name. */
        pcFileName = ( char * ) FreeRTOS_CLIGetParameter
                     (
            pcCommandString,        /* The command string itself. */
            1,                      /* Return the first parameter. */
            &xParameterStringLength /* Store the parameter string length. */
                     );
        configASSERT( pcFileName );

        /* Terminate the string. */
        pcFileName[ xParameterStringLength ] = 0x00;
    }

    if( xGoodParameters )
    {
        /* Attempt to open the requested file. */
        lFildes = red_open( pcFileName, RED_O_WRONLY | RED_O_APPEND | RED_O_CREAT );

        if( lFildes == -1 )
        {
            /* User-friendly messages for common errors. */
            switch( red_errno )
            {
                case RED_ENOENT:
                case RED_ENOTDIR:
                    strcpy( pcWriteBuffer, "Bad file path." );
                    break;

                case RED_EISDIR:
                    strcpy( pcWriteBuffer, "Cannot append to a directory." );
                    break;

                default:
                    sprintf( pcWriteBuffer, "Error %d opening file.", ( int ) red_errno );
                    break;
            }
        }
        else
        {
            /* Put the requested character into the buffer. */
            memset( pcWriteBuffer, pcCharacter[ 0 ], xWriteBufferLen );

            /* Append the data. */
            for( lTotalWritten = 0; lTotalWritten < lAppendLength; lTotalWritten += lThisWrite )
            {
                lThisWrite = lAppendLength - lTotalWritten;

                if( lThisWrite > ( int32_t ) xWriteBufferLen )
                {
                    lThisWrite = ( int32_t ) xWriteBufferLen;
                }

                lBytesWritten = red_write( lFildes, pcWriteBuffer, lThisWrite );

                if( lBytesWritten == -1 )
                {
                    /* User-friendly messages for common errors. */
                    switch( red_errno )
                    {
                        case RED_ENOSPC:
                            strcpy( pcWriteBuffer, "Out of disk space." );
                            break;

                        default:
                            sprintf( pcWriteBuffer, "Error %d writing to file.", ( int ) red_errno );
                            break;
                    }

                    break;
                }
                else if( lBytesWritten != lThisWrite )
                {
                    /*	Some data was written, but not all of it.  This only
                     *  happens when the disk is full or the file reached its
                     *  maximum size.  That latter is unlikely in this demo. */
                    strcpy( pcWriteBuffer, "Out of disk space." );
                    break;
                }
            }

            if( lTotalWritten == lAppendLength )
            {
                strcpy( pcWriteBuffer, "Append successful." );
            }

            red_close( lFildes );
        }
    }

    strcat( pcWriteBuffer, cliNEW_LINE );

    return pdFALSE;
}
/*-----------------------------------------------------------*/

static BaseType_t prvDELCommand( char * pcWriteBuffer,
                                 size_t xWriteBufferLen,
                                 const char * pcCommandString )
{
    const char * pcParameter;
    BaseType_t xParameterStringLength;
    int32_t lStatus;

    /* This function assumes xWriteBufferLen is large enough! */
    ( void ) xWriteBufferLen;

    /* Obtain the parameter string. */
    pcParameter = FreeRTOS_CLIGetParameter
                  (
        pcCommandString,        /* The command string itself. */
        1,                      /* Return the first parameter. */
        &xParameterStringLength /* Store the parameter string length. */
                  );

    /* Sanity check something was returned. */
    configASSERT( pcParameter );

    /* Attempt to delete the file or directory. */
    lStatus = red_unlink( pcParameter );

    if( lStatus == 0 )
    {
        sprintf( pcWriteBuffer, "%s was deleted", pcParameter );
    }
    else
    {
        /* User-friendly messages for common errors. */
        switch( red_errno )
        {
            case RED_ENOTDIR:
            case RED_ENOENT:
                sprintf( pcWriteBuffer, "File not found." );
                break;

            case RED_ENOTEMPTY:
                sprintf( pcWriteBuffer, "Cannot remove directory: not empty." );
                break;

            default:
                sprintf( pcWriteBuffer, "Error %d deleting file.", ( int ) red_errno );
                break;
        }
    }

    strcat( pcWriteBuffer, cliNEW_LINE );

    return pdFALSE;
}
/*-----------------------------------------------------------*/

static BaseType_t prvCOPYCommand( char * pcWriteBuffer,
                                  size_t xWriteBufferLen,
                                  const char * pcCommandString )
{
    char * pcSourceFile;
    const char * pcDestinationFile;
    BaseType_t xParameterStringLength;
    int32_t lSourceFildes, lDestinationFildes;

    /* Obtain the name of the destination file. */
    pcDestinationFile = FreeRTOS_CLIGetParameter
                        (
        pcCommandString,        /* The command string itself. */
        2,                      /* Return the second parameter. */
        &xParameterStringLength /* Store the parameter string length. */
                        );

    /* Sanity check something was returned. */
    configASSERT( pcDestinationFile );

    /* Obtain the name of the source file. */
    pcSourceFile = ( char * ) FreeRTOS_CLIGetParameter
                   (
        pcCommandString,        /* The command string itself. */
        1,                      /* Return the first parameter. */
        &xParameterStringLength /* Store the parameter string length. */
                   );

    /* Sanity check something was returned. */
    configASSERT( pcSourceFile );

    /* Terminate the string. */
    pcSourceFile[ xParameterStringLength ] = 0x00;

    /* See if the source file exists, open it if it does. */
    lSourceFildes = red_open( pcSourceFile, RED_O_RDONLY );

    if( lSourceFildes == -1 )
    {
        sprintf( pcWriteBuffer, "Source file does not exist" );
    }
    else
    {
        /* Create the destination file, error if it already exists. */
        lDestinationFildes = red_open( pcDestinationFile, RED_O_CREAT | RED_O_EXCL | RED_O_WRONLY );

        if( lDestinationFildes == -1 )
        {
            sprintf( pcWriteBuffer, "Error: Destination file already exists" );
        }
        else
        {
            if( prvPerformCopy( lSourceFildes, lDestinationFildes, pcWriteBuffer, xWriteBufferLen ) == pdPASS )
            {
                sprintf( pcWriteBuffer, "Copy made" );
            }
            else
            {
                sprintf( pcWriteBuffer, "Error during copy" );
            }

            red_close( lDestinationFildes );
        }

        red_close( lSourceFildes );
    }

    strcat( pcWriteBuffer, cliNEW_LINE );

    return pdFALSE;
}
/*-----------------------------------------------------------*/

static BaseType_t prvCREATECommand( char * pcWriteBuffer,
                                    size_t xWriteBufferLen,
                                    const char * pcCommandString )
{
    const char * pcParameter;
    BaseType_t xParameterStringLength;
    int32_t lFildes;

    /* This function assumes xWriteBufferLen is large enough! */
    ( void ) xWriteBufferLen;

    /* Obtain the parameter string. */
    pcParameter = FreeRTOS_CLIGetParameter
                  (
        pcCommandString,        /* The command string itself. */
        1,                      /* Return the first parameter. */
        &xParameterStringLength /* Store the parameter string length. */
                  );

    /* Sanity check something was returned. */
    configASSERT( pcParameter );

    /* Attempt to create the file. */
    lFildes = red_open( pcParameter, RED_O_CREAT | RED_O_EXCL | RED_O_RDWR );

    if( lFildes != -1 )
    {
        sprintf( pcWriteBuffer, "%s was created", pcParameter );
        red_close( lFildes );
    }
    else
    {
        /* User-friendly messages for common errors. */
        switch( red_errno )
        {
            case RED_ENOTDIR:
            case RED_ENOENT:
                sprintf( pcWriteBuffer, "Bad file path." );
                break;

            case RED_EEXIST:
                sprintf( pcWriteBuffer, "File already exists." );
                break;

            default:
                sprintf( pcWriteBuffer, "Error %d creating file.", ( int ) red_errno );
                break;
        }
    }

    strcat( pcWriteBuffer, cliNEW_LINE );

    return pdFALSE;
}
/*-----------------------------------------------------------*/

static BaseType_t prvMKDIRCommand( char * pcWriteBuffer,
                                   size_t xWriteBufferLen,
                                   const char * pcCommandString )
{
    const char * pcParameter;
    BaseType_t xParameterStringLength;
    int32_t lStatus;

    /* This function assumes xWriteBufferLen is large enough! */
    ( void ) xWriteBufferLen;

    /* Obtain the parameter string. */
    pcParameter = FreeRTOS_CLIGetParameter
                  (
        pcCommandString,        /* The command string itself. */
        1,                      /* Return the first parameter. */
        &xParameterStringLength /* Store the parameter string length. */
                  );

    /* Sanity check something was returned. */
    configASSERT( pcParameter );

    /* Attempt to create the file. */
    lStatus = red_mkdir( pcParameter );

    if( lStatus == 0 )
    {
        sprintf( pcWriteBuffer, "%s was created", pcParameter );
    }
    else
    {
        /* User-friendly messages for common errors. */
        switch( red_errno )
        {
            case RED_ENOTDIR:
            case RED_ENOENT:
                sprintf( pcWriteBuffer, "Bad file path." );
                break;

            case RED_EEXIST:
                sprintf( pcWriteBuffer, "Directory already exists." );
                break;

            default:
                sprintf( pcWriteBuffer, "Error %d creating directory.", ( int ) red_errno );
                break;
        }
    }

    strcat( pcWriteBuffer, cliNEW_LINE );

    return pdFALSE;
}
/*-----------------------------------------------------------*/

static BaseType_t prvRENAMECommand( char * pcWriteBuffer,
                                    size_t xWriteBufferLen,
                                    const char * pcCommandString )
{
    const char * pcDestinationFile;
    char * pcSourceFile;
    BaseType_t xParameterStringLength;
    int32_t lStatus;

    /* This function assumes xWriteBufferLen is large enough! */
    ( void ) xWriteBufferLen;

    /* Obtain the name of the destination file. */
    pcDestinationFile = FreeRTOS_CLIGetParameter
                        (
        pcCommandString,        /* The command string itself. */
        2,                      /* Return the second parameter. */
        &xParameterStringLength /* Store the parameter string length. */
                        );

    /* Sanity check something was returned. */
    configASSERT( pcDestinationFile );

    /* Obtain the name of the source file. */
    pcSourceFile = ( char * ) FreeRTOS_CLIGetParameter
                   (
        pcCommandString,        /* The command string itself. */
        1,                      /* Return the first parameter. */
        &xParameterStringLength /* Store the parameter string length. */
                   );

    /* Sanity check something was returned. */
    configASSERT( pcSourceFile );

    /* Terminate the string. */
    pcSourceFile[ xParameterStringLength ] = 0x00;

    /* Attempt to rename the file. */
    lStatus = red_rename( pcSourceFile, pcDestinationFile );

    if( lStatus == 0 )
    {
        sprintf( pcWriteBuffer, "%s was renamed to %s", pcSourceFile, pcDestinationFile );
    }
    else
    {
        /* User-friendly messages for common errors. */
        switch( red_errno )
        {
            case RED_ENOTDIR:
            case RED_ENOENT:
            case RED_EISDIR:
                sprintf( pcWriteBuffer, "Bad file path." );
                break;

            /* This will only be seen if POSIX rename is disabled. */
            case RED_EEXIST:
                sprintf( pcWriteBuffer, "Destination already exists." );
                break;

            default:
                sprintf( pcWriteBuffer, "Error %d renaming file.", ( int ) red_errno );
                break;
        }
    }

    strcat( pcWriteBuffer, cliNEW_LINE );

    return pdFALSE;
}
/*-----------------------------------------------------------*/

static BaseType_t prvLINKCommand( char * pcWriteBuffer,
                                  size_t xWriteBufferLen,
                                  const char * pcCommandString )
{
    const char * pcDestinationFile;
    char * pcSourceFile;
    BaseType_t xParameterStringLength;
    int32_t lStatus;

    /* This function assumes xWriteBufferLen is large enough! */
    ( void ) xWriteBufferLen;

    /* Obtain the name of the destination file. */
    pcDestinationFile = FreeRTOS_CLIGetParameter
                        (
        pcCommandString,        /* The command string itself. */
        2,                      /* Return the second parameter. */
        &xParameterStringLength /* Store the parameter string length. */
                        );

    /* Sanity check something was returned. */
    configASSERT( pcDestinationFile );

    /* Obtain the name of the source file. */
    pcSourceFile = ( char * ) FreeRTOS_CLIGetParameter
                   (
        pcCommandString,        /* The command string itself. */
        1,                      /* Return the first parameter. */
        &xParameterStringLength /* Store the parameter string length. */
                   );

    /* Sanity check something was returned. */
    configASSERT( pcSourceFile );

    /* Terminate the string. */
    pcSourceFile[ xParameterStringLength ] = 0x00;

    /* Attempt to create the hard link. */
    lStatus = red_link( pcSourceFile, pcDestinationFile );

    if( lStatus == 0 )
    {
        sprintf( pcWriteBuffer, "%s was linked to %s", pcDestinationFile, pcSourceFile );
    }
    else
    {
        /* User-friendly messages for common errors. */
        switch( red_errno )
        {
            case RED_ENOTDIR:
            case RED_ENOENT:
                sprintf( pcWriteBuffer, "Bad file path." );
                break;

            case RED_EPERM:
                sprintf( pcWriteBuffer, "Cannot link a directory." );
                break;

            case RED_EMLINK:
                sprintf( pcWriteBuffer, "Too many hard links." );
                break;

            default:
                sprintf( pcWriteBuffer, "Error %d linking file.", ( int ) red_errno );
                break;
        }
    }

    strcat( pcWriteBuffer, cliNEW_LINE );

    return pdFALSE;
}
/*-----------------------------------------------------------*/

static BaseType_t prvSTATCommand( char * pcWriteBuffer,
                                  size_t xWriteBufferLen,
                                  const char * pcCommandString )
{
    const char * pcParameter, * pcModeString;
    BaseType_t xParameterStringLength;
    REDSTAT finfo;
    int32_t lFildes, lStatus;

    /* Ensure the buffer leaves space for the \r\n. */
    configASSERT( xWriteBufferLen > ( strlen( cliNEW_LINE ) * 2 ) );
    xWriteBufferLen -= strlen( cliNEW_LINE );

    /* Find the file name. */
    pcParameter = FreeRTOS_CLIGetParameter
                  (
        pcCommandString,        /* The command string itself. */
        1,                      /* Return the first parameter. */
        &xParameterStringLength /* Store the parameter string length. */
                  );

    /* Sanity check something was returned. */
    configASSERT( pcParameter );

    /* Attempt to open the requested file. */
    lFildes = red_open( pcParameter, RED_O_RDONLY );

    if( lFildes == -1 )
    {
        /* User-friendly messages for common errors. */
        switch( red_errno )
        {
            case RED_ENOENT:
            case RED_ENOTDIR:
                snprintf( pcWriteBuffer, xWriteBufferLen, "File not found." );
                break;

            default:
                snprintf( pcWriteBuffer, xWriteBufferLen, "Error %d opening file.", ( int ) red_errno );
                break;
        }
    }
    else
    {
        lStatus = red_fstat( lFildes, &finfo );

        if( lStatus == 0 )
        {
            if( RED_S_ISDIR( finfo.st_mode ) )
            {
                pcModeString = "dir";
            }
            else
            {
                pcModeString = "file";
            }

            snprintf( pcWriteBuffer, xWriteBufferLen, "ino=%lu mode=0x%04x(%s) nlink=%x size=%lu blocks=%lu",
                      ( unsigned long ) finfo.st_ino, ( unsigned ) finfo.st_mode, pcModeString,
                      ( unsigned ) finfo.st_nlink, ( unsigned long ) finfo.st_size, ( unsigned long ) finfo.st_blocks );
        }
        else
        {
            snprintf( pcWriteBuffer, xWriteBufferLen, "Error %d querying file.", ( int ) red_errno );
        }

        red_close( lFildes );
    }

    strcat( pcWriteBuffer, cliNEW_LINE );

    return pdFALSE;
}
/*-----------------------------------------------------------*/

static BaseType_t prvSTATFSCommand( char * pcWriteBuffer,
                                    size_t xWriteBufferLen,
                                    const char * pcCommandString )
{
    REDSTATFS fsinfo;
    int32_t lStatus;

    /* Avoid compiler warnings. */
    ( void ) pcCommandString;

    /* Ensure the buffer leaves space for the \r\n. */
    configASSERT( xWriteBufferLen > ( strlen( cliNEW_LINE ) * 2 ) );
    xWriteBufferLen -= strlen( cliNEW_LINE );

    lStatus = red_statvfs( "", &fsinfo );

    if( lStatus == -1 )
    {
        snprintf( pcWriteBuffer, xWriteBufferLen, "Error %d querying file system.", ( int ) red_errno );
    }
    else
    {
        snprintf( pcWriteBuffer, xWriteBufferLen,
                  "Block size: %lu\r\n"
                  "Block count: %lu\r\n"
                  "Free blocks: %lu\r\n"
                  "Inode count: %lu\r\n"
                  "Free inodes: %lu\r\n",
                  ( unsigned long ) fsinfo.f_bsize, ( unsigned long ) fsinfo.f_blocks,
                  ( unsigned long ) fsinfo.f_bfree, ( unsigned long ) fsinfo.f_files,
                  ( unsigned long ) fsinfo.f_ffree );
    }

    strcat( pcWriteBuffer, cliNEW_LINE );

    return pdFALSE;
}
/*-----------------------------------------------------------*/

static BaseType_t prvFORMATCommand( char * pcWriteBuffer,
                                    size_t xWriteBufferLen,
                                    const char * pcCommandString )
{
    int32_t lStatus;

    /* Avoid compiler warnings. */
    ( void ) pcCommandString;

    /* This function assumes xWriteBufferLen is large enough! */
    ( void ) xWriteBufferLen;

    /* File system volumes cannot be formatted while mounted. */
    lStatus = red_umount( "" );

    if( lStatus == -1 )
    {
        sprintf( pcWriteBuffer, "Error %d during unmount.", ( int ) red_errno );
    }
    else
    {
        /* Re-format the file system volume. */
        lStatus = red_format( "" );

        if( lStatus == -1 )
        {
            sprintf( pcWriteBuffer, "Error %d during format.", ( int ) red_errno );
        }
        else
        {
            /* Mount again so that other commands will work properly. */
            lStatus = red_mount( "" );

            if( lStatus == -1 )
            {
                sprintf( pcWriteBuffer, "Error %d during mount.", ( int ) red_errno );
            }
            else
            {
                strcpy( pcWriteBuffer, "Format successful." );
            }
        }
    }

    strcat( pcWriteBuffer, cliNEW_LINE );

    return pdFALSE;
}
/*-----------------------------------------------------------*/

static BaseType_t prvTRANSACTCommand( char * pcWriteBuffer,
                                      size_t xWriteBufferLen,
                                      const char * pcCommandString )
{
    int32_t lStatus;

    /* Avoid compiler warnings. */
    ( void ) pcCommandString;

    /* This function assumes xWriteBufferLen is large enough! */
    ( void ) xWriteBufferLen;

    /* Save the original transaction mask settings. */
    lStatus = red_transact( "" );

    if( lStatus == -1 )
    {
        sprintf( pcWriteBuffer, "Error %d during transaction point.", ( int ) red_errno );
    }
    else
    {
        strcpy( pcWriteBuffer, "Transaction point successful." );
    }

    strcat( pcWriteBuffer, cliNEW_LINE );

    return pdFALSE;
}
/*-----------------------------------------------------------*/

static BaseType_t prvTRANSMASKGETCommand( char * pcWriteBuffer,
                                          size_t xWriteBufferLen,
                                          const char * pcCommandString )
{
    uint32_t ulEventMask;
    int32_t lStatus;

    /* Avoid compiler warnings. */
    ( void ) pcCommandString;

    /* Ensure the buffer leaves space for the \r\n. */
    configASSERT( xWriteBufferLen > ( strlen( cliNEW_LINE ) * 2 ) );
    xWriteBufferLen -= strlen( cliNEW_LINE );

    lStatus = red_gettransmask( "", &ulEventMask );

    if( lStatus == -1 )
    {
        snprintf( pcWriteBuffer, xWriteBufferLen, "Error %d retrieving automatic transaction event mask.", ( int ) red_errno );
    }
    else
    {
        snprintf( pcWriteBuffer, xWriteBufferLen,
                  "Current automatic transaction event mask: 0x%04lx\r\n"
                  "RED_TRANSACT_UMOUNT   (0x0001): %s\r\n"
                  "RED_TRANSACT_CREAT    (0x0002): %s\r\n"
                  "RED_TRANSACT_UNLINK   (0x0004): %s\r\n"
                  "RED_TRANSACT_MKDIR    (0x0008): %s\r\n"
                  "RED_TRANSACT_RENAME   (0x0010): %s\r\n"
                  "RED_TRANSACT_LINK     (0x0020): %s\r\n"
                  "RED_TRANSACT_CLOSE    (0x0040): %s\r\n"
                  "RED_TRANSACT_WRITE    (0x0080): %s\r\n"
                  "RED_TRANSACT_FSYNC    (0x0100): %s\r\n"
                  "RED_TRANSACT_TRUNCATE (0x0200): %s\r\n"
                  "RED_TRANSACT_VOLFULL  (0x0400): %s\r\n",
                  ( unsigned long ) ulEventMask,
                  ( ulEventMask & RED_TRANSACT_UMOUNT ) ? "Enabled" : "Disabled",
                  ( ulEventMask & RED_TRANSACT_CREAT ) ? "Enabled" : "Disabled",
                  ( ulEventMask & RED_TRANSACT_UNLINK ) ? "Enabled" : "Disabled",
                  ( ulEventMask & RED_TRANSACT_MKDIR ) ? "Enabled" : "Disabled",
                  ( ulEventMask & RED_TRANSACT_RENAME ) ? "Enabled" : "Disabled",
                  ( ulEventMask & RED_TRANSACT_LINK ) ? "Enabled" : "Disabled",
                  ( ulEventMask & RED_TRANSACT_CLOSE ) ? "Enabled" : "Disabled",
                  ( ulEventMask & RED_TRANSACT_WRITE ) ? "Enabled" : "Disabled",
                  ( ulEventMask & RED_TRANSACT_FSYNC ) ? "Enabled" : "Disabled",
                  ( ulEventMask & RED_TRANSACT_TRUNCATE ) ? "Enabled" : "Disabled",
                  ( ulEventMask & RED_TRANSACT_VOLFULL ) ? "Enabled" : "Disabled" );
    }

    strcat( pcWriteBuffer, cliNEW_LINE );

    return pdFALSE;
}
/*-----------------------------------------------------------*/

static BaseType_t prvTRANSMASKSETCommand( char * pcWriteBuffer,
                                          size_t xWriteBufferLen,
                                          const char * pcCommandString )
{
    const char * pcParameter;
    BaseType_t xParameterStringLength;
    uint32_t ulEventMask;
    int32_t lStatus;

    /* Ensure the buffer leaves space for the \r\n. */
    configASSERT( xWriteBufferLen > ( strlen( cliNEW_LINE ) * 2 ) );
    xWriteBufferLen -= strlen( cliNEW_LINE );

    /* Obtain the parameter string. */
    pcParameter = FreeRTOS_CLIGetParameter
                  (
        pcCommandString,        /* The command string itself. */
        1,                      /* Return the first parameter. */
        &xParameterStringLength /* Store the parameter string length. */
                  );

    /* Sanity check something was returned. */
    configASSERT( pcParameter );

    if( ( pcParameter[ 0 ] == '0' ) && ( ( pcParameter[ 1 ] == 'x' ) || ( pcParameter[ 1 ] == 'X' ) ) )
    {
        pcParameter += 2;
    }

    /* Convert the argument into a value. */
    RedHtoUL( pcParameter, &ulEventMask );

    /* Set the new transaction mask. */
    lStatus = red_settransmask( "", ulEventMask );

    if( lStatus == -1 )
    {
        /* User-friendly messages for common errors. */
        switch( red_errno )
        {
            case RED_EINVAL:
                snprintf( pcWriteBuffer, xWriteBufferLen, "Invalid bits in transaction mask." );
                break;

            default:
                snprintf( pcWriteBuffer, xWriteBufferLen, "Error %d setting transaction mask.", ( int ) red_errno );
                break;
        }
    }
    else
    {
        snprintf( pcWriteBuffer, xWriteBufferLen,
                  "Successfully set automatic transaction mask.  Enabled events:\r\n"
                  "RED_TRANSACT_UMOUNT   (0x0001): %s\r\n"
                  "RED_TRANSACT_CREAT    (0x0002): %s\r\n"
                  "RED_TRANSACT_UNLINK   (0x0004): %s\r\n"
                  "RED_TRANSACT_MKDIR    (0x0008): %s\r\n"
                  "RED_TRANSACT_RENAME   (0x0010): %s\r\n"
                  "RED_TRANSACT_LINK     (0x0020): %s\r\n"
                  "RED_TRANSACT_CLOSE    (0x0040): %s\r\n"
                  "RED_TRANSACT_WRITE    (0x0080): %s\r\n"
                  "RED_TRANSACT_FSYNC    (0x0100): %s\r\n"
                  "RED_TRANSACT_TRUNCATE (0x0200): %s\r\n"
                  "RED_TRANSACT_VOLFULL  (0x0400): %s\r\n",
                  ( ulEventMask & RED_TRANSACT_UMOUNT ) ? "Enabled" : "Disabled",
                  ( ulEventMask & RED_TRANSACT_CREAT ) ? "Enabled" : "Disabled",
                  ( ulEventMask & RED_TRANSACT_UNLINK ) ? "Enabled" : "Disabled",
                  ( ulEventMask & RED_TRANSACT_MKDIR ) ? "Enabled" : "Disabled",
                  ( ulEventMask & RED_TRANSACT_RENAME ) ? "Enabled" : "Disabled",
                  ( ulEventMask & RED_TRANSACT_LINK ) ? "Enabled" : "Disabled",
                  ( ulEventMask & RED_TRANSACT_CLOSE ) ? "Enabled" : "Disabled",
                  ( ulEventMask & RED_TRANSACT_WRITE ) ? "Enabled" : "Disabled",
                  ( ulEventMask & RED_TRANSACT_FSYNC ) ? "Enabled" : "Disabled",
                  ( ulEventMask & RED_TRANSACT_TRUNCATE ) ? "Enabled" : "Disabled",
                  ( ulEventMask & RED_TRANSACT_VOLFULL ) ? "Enabled" : "Disabled" );
    }

    strcat( pcWriteBuffer, cliNEW_LINE );

    return pdFALSE;
}
/*-----------------------------------------------------------*/

static BaseType_t prvABORTCommand( char * pcWriteBuffer,
                                   size_t xWriteBufferLen,
                                   const char * pcCommandString )
{
    uint32_t ulEventMask;
    int32_t lStatus;

    /* Avoid compiler warnings. */
    ( void ) pcCommandString;

    /* This function assumes xWriteBufferLen is large enough! */
    ( void ) xWriteBufferLen;

    /* Save the original transaction mask settings. */
    lStatus = red_gettransmask( "", &ulEventMask );

    if( lStatus == -1 )
    {
        sprintf( pcWriteBuffer, "Error %d querying transaction mask.", ( int ) red_errno );
    }
    else
    {
        /* Make it so that red_umount() will not automatically commit a new
         * transaction point. */
        lStatus = red_settransmask( "", ulEventMask & ~( ( uint32_t ) RED_TRANSACT_UMOUNT ) );

        if( lStatus == -1 )
        {
            sprintf( pcWriteBuffer, "Error %d setting transaction mask.", ( int ) red_errno );
        }
        else
        {
            /* Unmount.  Since red_umount() will not transact, all changes which
             * were not already transacted are rolled back. */
            lStatus = red_umount( "" );

            if( lStatus == -1 )
            {
                sprintf( pcWriteBuffer, "Error %d during unmount.", ( int ) red_errno );
            }
            else
            {
                /* Mount.  Mount always starts from the last transaction point. */
                lStatus = red_mount( "" );

                if( lStatus == -1 )
                {
                    sprintf( pcWriteBuffer, "Error %d during mount.", ( int ) red_errno );
                }
                else
                {
                    strcpy( pcWriteBuffer, "Working state changes successfully aborted." );
                }
            }

            /* Restore the original transaction mask settings. */
            red_settransmask( "", ulEventMask );
        }
    }

    strcat( pcWriteBuffer, cliNEW_LINE );

    return pdFALSE;
}
/*-----------------------------------------------------------*/

static BaseType_t prvTESTFSCommand( char * pcWriteBuffer,
                                    size_t xWriteBufferLen,
                                    const char * pcCommandString )
{
    UBaseType_t uxOriginalPriority;
    FSSTRESSPARAM param;

    /* Avoid compiler warnings. */
    ( void ) xWriteBufferLen;
    ( void ) pcCommandString;

    /* Limitations in the interaction with the Windows TCP/IP stack require
     * the command console to run at the idle priority.  Raise the priority for
     * the duration of the tests to ensure there are not multiple switches to the
     * idle task as in the simulated environment the idle task hook function may
     * include a (relatively) long delay. */
    uxOriginalPriority = uxTaskPriorityGet( NULL );
    vTaskPrioritySet( NULL, configMAX_PRIORITIES - 1 );

    /* Delete all files to avoid interfering with the test. */
    red_umount( "" );
    red_format( "" );
    red_mount( "" );

    FsstressDefaultParams( &param );
    param.fVerbose = pdTRUE;
    param.ulNops = 10000;
    param.ulSeed = 1;
    FsstressStart( &param );

    /* Clean up after the test. */
    red_umount( "" );
    red_format( "" );
    red_mount( "" );

    /* Reset back to the original priority. */
    vTaskPrioritySet( NULL, uxOriginalPriority );

    sprintf( pcWriteBuffer, "%s", "Test results were sent to Windows console" );
    strcat( pcWriteBuffer, cliNEW_LINE );

    return pdFALSE;
}
/*-----------------------------------------------------------*/

static BaseType_t prvPerformCopy( int32_t lSourceFildes,
                                  int32_t lDestinationFiledes,
                                  char * pxWriteBuffer,
                                  size_t xWriteBufferLen )
{
    int32_t lBytesRead;
    BaseType_t xReturn = pdPASS;

    /* Assuming both files are at offset zero. */

    for( ; ; )
    {
        /* Read the next block of data. */
        lBytesRead = red_read( lSourceFildes, pxWriteBuffer, xWriteBufferLen );

        if( lBytesRead <= 0 )
        {
            if( lBytesRead == -1 )
            {
                /* Error reading from file. */
                xReturn = pdFAIL;
            }
            else
            {
                /* No error: reached end of file, time to stop. */
            }

            break;
        }

        /* Write the block of data to the end of the file. */
        if( red_write( lDestinationFiledes, pxWriteBuffer, lBytesRead ) != lBytesRead )
        {
            xReturn = pdFAIL;
            break;
        }
    }

    return xReturn;
}
/*-----------------------------------------------------------*/

static void prvCreateFileInfoString( char * pcBuffer,
                                     REDDIRENT * pxDirent )
{
    const char * pcFile = "file", * pcDirectory = "directory";
    const char * pcAttrib;

    /* Point pcAttrib to a string that describes the file. */
    if( RED_S_ISDIR( pxDirent->d_stat.st_mode ) )
    {
        pcAttrib = pcDirectory;
    }
    else
    {
        pcAttrib = pcFile;
    }

    /* Create a string that includes the file name, the file size and the
     * attributes string. */
    sprintf( pcBuffer, "%s [%s] [size=%lld]", pxDirent->d_name, pcAttrib, pxDirent->d_stat.st_size );
}
